package czc.hly.spring.formework.context;

import czc.hly.spring.demo.action.MyAction;
import czc.hly.spring.formework.annotation.GPAutowired;
import czc.hly.spring.formework.annotation.GPController;
import czc.hly.spring.formework.annotation.GPService;
import czc.hly.spring.formework.beans.BeanDefinition;
import czc.hly.spring.formework.beans.BeanPostProcessor;
import czc.hly.spring.formework.beans.BeanWrapper;
import czc.hly.spring.formework.context.support.BeanDefinitionReader;
import czc.hly.spring.formework.core.BeanFactory;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class GPApplicationContext implements BeanFactory {

    private String[] configLocations;

    private BeanDefinitionReader reader;

    //用来保存配置信息的
    private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String,BeanDefinition>();

    //用来保证注册式单例
    private Map<String,Object> beanCacheMap = new HashMap<>();

    //存储所有被代理过的对象
    private Map<String,BeanWrapper> beanWrapperMap = new ConcurrentHashMap<>();


    public GPApplicationContext(String ... configLocations){
        this.configLocations = configLocations;
        this.refresh();
    }

    public void refresh(){

        //定位
        this.reader = new BeanDefinitionReader(configLocations);
        //加载
        List<String> beanDefinitions =  reader.loadBeanDefinitions();
        //注册
        doRegisty(beanDefinitions);
        //依赖注入
        //（lazy-init = false）,要执行依赖注入
        // 在这里自动调用getBean方法
        doAutorited();
        System.out.println("--------------------------------------------");
        //测试
        MyAction myAction = (MyAction)this.getBean("myAction");
        try {
            myAction.query();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


    }

    private void doAutorited() {

        System.out.println("one_beanWrapperMap:"+beanWrapperMap);

        for(Map.Entry<String,BeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()){
            String beanName = beanDefinitionEntry.getKey();
            System.out.println("beanName = "+beanName);
            System.out.println(beanDefinitionEntry.getValue());
            if(!beanDefinitionEntry.getValue().isLazyInit()){
                Object wrapperInstance = getBean(beanName);

            }

        }

        System.out.println("two_beanWrapperMap:"+beanWrapperMap);

        for(Map.Entry<String,BeanWrapper> beanWrapperEntry : this.beanWrapperMap.entrySet()){
            populateBean(beanWrapperEntry.getKey(),beanWrapperEntry.getValue().getWrapperInstance());

        }

        System.out.println("three_beanWrapperMap:"+beanWrapperMap);

    }


    public void populateBean(String beanName,Object instance){
        Class clazz = instance.getClass();
        //不是所有牛奶都叫特仑苏
        if(!(clazz.isAnnotationPresent(GPController.class) ||
                clazz.isAnnotationPresent(GPService.class))){
            return;
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(GPAutowired.class)){ continue; }

            GPAutowired autowired = field.getAnnotation(GPAutowired.class);

            String autowiredBeanName = autowired.value().trim();

            if("".equals(autowiredBeanName)){
                System.out.println("field.getType():"+field.getType());
                System.out.println("field.getType().getName():"+field.getType().getName());
                autowiredBeanName = field.getType().getName();
            }

            field.setAccessible(true);

            try {

                //System.out.println("=======================" +instance +"," + autowiredBeanName + "," + this.beanWrapperMap.get(autowiredBeanName));
                System.out.println("this.beanWrapperMap.get(autowiredBeanName).getWrapperInstance():"+this.beanWrapperMap.get(autowiredBeanName).getWrapperInstance());
                field.set(instance,this.beanWrapperMap.get(autowiredBeanName).getWrapperInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }

    }


    //真正的将BeanDefinition注册到IOC容器中（beanDefinitionMap）
    private void doRegisty(List<String> beanDefinitions) {
        //beanName 有三种情况

        //1.默认是类名首字母小写

        //2.自定义名字

        //3.接口注入
        try {
            for(String className : beanDefinitions){

                Class<?> beanClass = Class.forName(className);

                //如果是一个接口，是不能实例化的
                //用它实现类来实例化
                if (beanClass.isInterface()){continue;}

                BeanDefinition beanDefinition = reader.registerBean(className);
                if (beanDefinition != null) {
                    System.out.println("beanDefinition.getFactoryBeanName():"+beanDefinition.getFactoryBeanName()); //queryService
                    this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
                }

                Class<?>[] interfaces = beanClass.getInterfaces();
                for (Class<?> i : interfaces){
                    //如果是多个实现类只能覆盖
                    //为什么？因为spring没那么智能
                    //这个时候，可以自定义名字
                    System.out.println("i.getName():"+i.getName());  //czc.hly.spring.demo.service.IQueryService
                    this.beanDefinitionMap.put(i.getName(),beanDefinition);
                }

                //到这里为止,容器初始化完毕

            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }
    //通过读取BeanDefinition中的信息
    //然后，通过反射机制创建一个实例并返回
    //spring做法是，不会把最原始的对象放出去，会用一个BeanWrapper来进行一次包装
    //包装器模式：
    //1.保留原来的OOP关系
    //2.我需要对它进行扩展，增强（为了以后的AOP打基础）
    @Override
    public Object getBean(String beanName) {

        BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);

//        String className = beanDefinition.getBeanClassName();

        try {

            //生成通知事件
            BeanPostProcessor beanPostProcessor = new BeanPostProcessor();


            Object instance = instantionBean(beanDefinition);
            if (null == instance){return null; }
            //在实例初始化以前调用一次
            beanPostProcessor.postProcessBeforeInitialization(instance,beanName);

            BeanWrapper beanWrapper = new BeanWrapper(instance);
            beanWrapper.setPostProcessor(beanPostProcessor);
            this.beanWrapperMap.put(beanName,beanWrapper);
            //在实例初始化以后调用一次
            beanPostProcessor.postProcessAfterInitialization(instance,beanName);

            //通过这样一调用，相当于给我们自己留有了可操作的空间
            return this.beanWrapperMap.get(beanName).getWrapperInstance();



        }catch (Exception e){
            e.printStackTrace();
        }

        return null;
    }

    //传一个BeanDefinition，就返回一个实例Bean
    private Object instantionBean(BeanDefinition beanDefinition){
        Object instance = null;
        String className = beanDefinition.getBeanClassName();
        try {
            //因为根据class才能确定一个类是否有实例
            if (this.beanCacheMap.containsKey(className)){
                instance = this.beanCacheMap.get(className);
            }else {
                Class clazz = Class.forName(className);
                System.out.println("============="+clazz);
                instance = clazz.newInstance();
                System.out.println("###########"+instance);
                this.beanCacheMap.put(className,instance);
            }
            return instance;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

}
